         ;代码清单9-2
         ;文件名：c09.asm
         ;文件说明：用户程序 
         ;创建日期：2011-5-5 18:17，修改于2021-10-3 15:35
         
;===============================================================================
SECTION header vstart=0                     ;定义用户程序头部段 
    program_length  dd program_end          ;程序总长度[0x00]
    
    ;用户程序入口点
    code_entry      dw start                ;偏移地址[0x04]
                    dd section.code_1.start ;段地址[0x06] 
    
    realloc_tbl_len dw (header_end-code_1_segment)/4
                                            ;段重定位表项个数[0x0a]
    
    ;段重定位表           
    code_1_segment  dd section.code_1.start ;[0x0c]
    code_2_segment  dd section.code_2.start ;[0x10]
    data_1_segment  dd section.data_1.start ;[0x14]
    data_2_segment  dd section.data_2.start ;[0x18]
    stack_segment   dd section.stack.start  ;[0x1c]
    
    header_end:                
    
;===============================================================================
SECTION code_1 align=16 vstart=0         ;定义代码段1（16字节对齐） 
put_string:                              ;显示串(0结尾)。
                                         ;输入：DS:BX=串地址
         mov cl,[bx]
         or cl,cl                        ;cl=0 ?
         jz .exit                        ;是的，返回主程序 
         call put_char
         inc bx                          ;下一个字符 
         jmp put_string

   .exit:
         ret

;-------------------------------------------------------------------------------
read_reg:
                                        ;input: al: index register
                                        ;output: al: value
        push dx
        mov dx,0x3d4
        out dx,al
        mov dx,0x3d5
        in al,dx
        pop dx
        ret

write_reg:
                                        ;input: al: index register
                                        ;       ah: value
        push dx
        push ax
        mov dx,0x3d4
        out dx,al
        mov dx,0x3d5
        mov al,ah
        out dx,al
        pop ax
        pop dx
        ret

get_cursor:
        ; output: ax: cursor index
         mov al,0x0e
         call read_reg
         mov ah,al        
         mov al,0xf
         call read_reg
         ret

set_cursor:
         ; input: bx: cursor index 
         mov al,0x0e
         mov ah,bh
         call write_reg 
         mov al,0x0f
         mov ah,bl
         call write_reg
         ret

put_char:                                ;显示一个字符
                                         ;输入：cl=字符ascii
         push ax
         push bx
         push cx
         push dx
         push ds
         push es

         call get_cursor	             ;AX=cursor index

         mov bx,ax                       ;BX=代表光标位置的16位数

         cmp cl,0x0d                     ;回车符？
         jnz .put_0a                     ;不是。看看是不是换行等字符 
         mov bl,80                       
         div bl
         mul bl
         mov bx,ax
         jmp .set_cursor

 .put_0a:
         cmp cl,0x0a                     ;换行符？
         jnz .put_other                  ;不是，那就正常显示字符 
         add bx,80
         jmp .roll_screen

 .put_other:                             ;正常显示字符
         mov ax,0xb800
         mov es,ax
         shl bx,1
         mov [es:bx],cl

         ;以下将光标位置推进一个字符
         shr bx,1
         add bx,1

 .roll_screen:
         cmp bx,2000                     ;光标超出屏幕？滚屏
         jl .set_cursor

         push bx

         mov ax,0xb800
         mov ds,ax
         mov es,ax
         cld
         mov si,0xa0
         mov di,0x00
         mov cx,80*24
         rep movsw
         mov bx,80*24*2                   ;清除屏幕最底一行
         mov cx,80
 .cls:
         mov word[es:bx],0x0720
         add bx,2
         loop .cls

         pop bx
         sub bx,80

 .set_cursor:
         call set_cursor	              ;bx=cursor index

         pop es
         pop ds
         pop dx
         pop cx
         pop bx
         pop ax

         ret

;-------------------------------------------------------------------------------
  start:
         ;初始执行时，DS和ES指向用户程序头部段
         mov ax,[stack_segment]           ;设置到用户程序自己的堆栈 
         mov ss,ax
         mov sp,stack_end
         
         mov ax,[data_1_segment]          ;设置到用户程序自己的数据段
         mov ds,ax

         mov bx,msg0
         call put_string                  ;显示第一段信息 

         ; get msg from code2
         mov di, msg1
         push word [es:code_2_segment]
         mov ax, get_msg
         push ax
         mov bp,sp
         call far [bp]
         
         ; show msg of code2
         mov bx, di
         call put_string                  ;显示第二段信息 

         jmp $ 

;===============================================================================
SECTION code_2 align=16 vstart=0          ;定义代码段2（16字节对齐）

  get_msg:
         ; input: ds:di     msg buffer

         push ax
         push si
         push di
         push es

         mov ax, [es:data_2_segment]
         mov es, ax
         xor si, si
         mov si, msg2

   .copy:
         mov ax, [es:si]
         mov [di], ax
         inc si
         inc di
         cmp ax, 0
         jnz .copy
         
         pop es
         pop di
         pop si
         pop ax

         retf 
         
;==============================================================================
SECTION data_1 align=16 vstart=0

    msg0 db '  This is NASM - the famous Netwide Assembler. '
         db 'Back at SourceForge and in intensive development! '
         db 'Get the current versions from http://www.nasm.us/.'
         db 0x0d,0x0a,0x0d,0x0a
         db '  Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a
         db '     xor dx,dx',0x0d,0x0a
         db '     xor ax,ax',0x0d,0x0a
         db '     xor cx,cx',0x0d,0x0a
         db '  @@:',0x0d,0x0a
         db '     inc cx',0x0d,0x0a
         db '     add ax,cx',0x0d,0x0a
         db '     adc dx,0',0x0d,0x0a
         db '     inc cx',0x0d,0x0a
         db '     cmp cx,1000',0x0d,0x0a
         db '     jle @@',0x0d,0x0a
         db '     ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a
         db '     dummy1', 0x0d,0x0a,
         db '     dummy2', 0x0d,0x0a,
         db '     dummy3', 0x0d,0x0a,
         db '     dummy4', 0x0d,0x0a,
         db '     dummy5', 0x0d,0x0a,
         db 0
    msg1:
         times 512 db 0

;===============================================================================
SECTION data_2 align=16 vstart=0

    msg2 db '  The above contents is written by LeeChung. '
         db '2011-05-06'
         db 0

;===============================================================================
SECTION stack align=16 vstart=0
           
         resb 256

stack_end:  

;===============================================================================
SECTION trail align=16
program_end:
